package zaphod;

import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;

/**
 * immutable
 * 
 * @author c0338027
 * 
 */
public class Polynomial {
	private final Map<Integer, Double> poly;
	private static final Comparator<Integer> decending = new Comparator<Integer>() {
		@Override
		public int compare(Integer o1, Integer o2) {
			return o2 - o1;
		}
	};

	public Polynomial(int exp, double coef) {
		TreeMap<Integer, Double> poly_ = new TreeMap<Integer, Double>(decending);
		poly_.put(exp, coef);
		poly = Collections.unmodifiableMap(poly_);
	}

	private Polynomial(Map<Integer, Double> input) {
		poly = Collections.unmodifiableMap(input);
	}

	public Polynomial plus(Polynomial a) {
		TreeMap<Integer, Double> result = new TreeMap<Integer, Double>(
				decending);
		result.putAll(this.poly);
		Set<Entry<Integer, Double>> entrySet = a.poly.entrySet();
		for (Entry<Integer, Double> e : entrySet) {
			Integer key = e.getKey();
			Double value = e.getValue();
			if (result.containsKey(key)) {
				result.put(key, result.get(key) + value);
			} else {
				result.put(key, value);
			}
		}
		return new Polynomial(result);
	}

	public Polynomial minus(Polynomial b) {
		TreeMap<Integer, Double> result = new TreeMap<Integer, Double>(
				decending);
		result.putAll(this.poly);
		Set<Entry<Integer, Double>> entrySet = b.poly.entrySet();
		for (Entry<Integer, Double> e : entrySet) {
			Integer key = e.getKey();
			Double value = e.getValue();
			if (result.containsKey(key)) {
				result.put(key, result.get(key) - value);
			} else {
				result.put(key, -value);
			}
		}
		return new Polynomial(result);
	}

	public Polynomial multiply(double number) {
		TreeMap<Integer, Double> result = new TreeMap<Integer, Double>(
				decending);
		result.putAll(this.poly);
		Set<Integer> keySet = result.keySet();
		for (int key : keySet) {
			result.put(key, result.get(key) * number);
		}
		return new Polynomial(result);
	}

	public Polynomial multiply(Polynomial a) {
		TreeMap<Integer, Double> result = new TreeMap<Integer, Double>(
				decending);
		Set<Integer> left = poly.keySet();
		Set<Integer> right = a.poly.keySet();
		Map<Integer, Double> ls = poly, rs = a.poly;
		for (int lk : left) {
			for (int rk : right) {
				if (result.containsKey(lk + rk)) {
					result.put(lk + rk, result.get(lk + rk) + ls.get(lk)
							* rs.get(rk));
				} else {
					result.put(lk + rk, ls.get(lk) * rs.get(rk));
				}
			}
		}
		return new Polynomial(result);
	}

	public double evaluate(double a) {
		double result = 0.0;
		Set<Integer> keySet = poly.keySet();
		for (int k : keySet) {
			result += Math.pow(a, k) * poly.get(k);
		}
		return result;
	}

	private static String term(int n) {
		StringBuilder sb = new StringBuilder();
		// boolean first = true;
		for (int i = 0; i < n; i++) {
			// if (first)
			// first = false;
			// else
			sb.append("*");
			sb.append("x");
		}

		return sb.toString();
	}

	@Override
	public String toString() {
		StringBuilder sb = new StringBuilder();
		Set<Integer> keySet = poly.keySet();
		boolean first = true;
		for (int key : keySet) {
			Double value = poly.get(key);
			if (first)
				first = false;
			else
				sb.append(value >= 0 ? "+" : "");
			sb.append(value);
			if (false) {
				sb.append(term(key));
			} else {
				sb.append("*x**");
				sb.append(key);
			}
		}
		return sb.toString();
	}
}
